package br.com.afi.util;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * Métodos úteis para introspeção (reflection).
 * 
 * @author André Ignacio
 * @version 1.0
 */
public class ReflectionUtil
{
	/**
	 * Executa o método informado na classe informada. A classe deve possuir um construtor default.
	 * 
	 * @param className Nome da Classe
	 * @param method Nome do Método
	 * @param parameterTypes Tipos dos paramêtros do método, null para nenhum
	 * @param parameterObjs Paramêtros dos método, null para nenhum
	 * @return Retorno do método, ou null caso o método seja void.
	 * @throws ClassNotFoundException Lançada quando a classe não existir
	 * @throws InstantiationException Lançada quando não for possível instanciar um objeto apartir
	 *             da classe
	 * @throws IllegalAccessException Lançada quando não for possível acessar o método.
	 * @throws SecurityException Lançada quando não for possível acessar o método.
	 * @throws NoSuchMethodException Lançada quando o método não existir.
	 * @throws IllegalArgumentException Lançada quando os parâmetros informados estiverem incorretos
	 * @throws InvocationTargetException Lançada quando não for possível invocar o método.
	 */
	public static Object invokeMethod( String className, String method, Class<?> parameterTypes[], Object parameterObjs[] ) throws ClassNotFoundException, InstantiationException, IllegalAccessException, SecurityException, NoSuchMethodException, IllegalArgumentException, InvocationTargetException
	{
		final Class<?> c = Class.forName( className );
		final Object obj = c.newInstance();
		final Method m = c.getMethod( method, parameterTypes );

		return m.invoke( obj, parameterObjs );
	}

	/**
	 * Obtém o nome do método get para o atributo informado.
	 * 
	 * @param field Nome do atributo
	 * @return O nome do método get
	 */
	public static String buildGetterMethod( String field )
	{
		String getterMethod = null;
		if ( field != null )
		{
			final String firstLetter = field.substring( 0, 1 );
			final String otherLetters = field.substring( 1, field.length() );
			getterMethod = "get" + firstLetter.toUpperCase() + otherLetters;
		}
		return getterMethod;
	}

	/**
	 * Obtém o nome do método get para o atributo informado.
	 * 
	 * @param field Atributo
	 * @return O nome do método get
	 */
	public static String buildGetterMethod( Field field )
	{
		return buildGetterMethod( field.getName() );
	}

	/**
	 * Executa o método getter (se existir) do objeto informado.
	 * 
	 * @param obj Objeto
	 * @param field Campo
	 * @return Retorno do método
	 * @throws NoSuchMethodException Lançada quando não for possível encontrar o método
	 * @throws IllegalAccessException Lançada quando não for possível acessar o método
	 * @throws InvocationTargetException Lançada quando não for possível executar o método
	 */
	public static Object invokeGetterMethod( Object obj, Field field ) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException
	{
		final String getterName = buildGetterMethod( field );
		final Class<?> clazz = obj.getClass();
		final Method method = clazz.getMethod( getterName );
		return method.invoke( obj );
	}

	/**
	 * Obtém todos os atributos do objeto específicado.
	 * 
	 * @param obj Objeto
	 * @return Atributos
	 */
	public static Field[] getDeclaredFields( Object obj )
	{
		final Class<?> clazz = obj.getClass();
		return clazz.getDeclaredFields();
	}

	/**
	 * Verifica se o campo informada possui a anotação informada.
	 * 
	 * @param field Atributo
	 * @param annotationClass Classe de uma anotação
	 * @return Verdadeiro se possuir a anotação informada
	 */
	@SuppressWarnings("unchecked")
	public static boolean isAnnotation( Field field, Class annotationClass )
	{
		return field.getAnnotation( annotationClass ) != null;
	}

}
